#include "serial_driver.h"
#include <legacymsp430.h>
#include <string.h>

static int mUartBitTime;//time for one bit
static int mUartHalfBitTime;//time for half a bit


static UartTxBuffer_s mTxBuffer;
volatile UartRxBuffer_s gRxBuffer;   //global rx buffer
volatile uint8_t gRxFlags;
volatile uint8_t gCritical;
static uint16_t mTxData;
static uint8_t mRxData;

//Private functions
static int BufferFull(UartTxBuffer_s* bufferPtr);

//helper functions
static int BufferFull(UartTxBuffer_s* bufferPtr)
{
   int ret_status = 0;
   uint8_t temp = bufferPtr->mHeadPtr - bufferPtr->mTailPtr ;
   if(temp == BUFF_SIZE + 1 )
   {
      ret_status = 1;//rturn 1 if buffer full
   }
   return ret_status;
}

void Uart_TimerA_Init(int baud_rate)
{
   memset((void*)&mTxBuffer,0,sizeof(UartTxBuffer_s));
   memset((void*)&gRxBuffer,0,sizeof(UartRxBuffer_s));
   mUartHalfBitTime = (SMCLK_SPEED / (baud_rate * 2));
   mUartBitTime = (SMCLK_SPEED / baud_rate);
   
   gCritical = 0;  //set locking to 0
   
   TACCTL0 = OUT;                          // Set TXD Idle = '1'
   TACCTL1 = SCS + CM1 + CAP + CCIE;       // Sync, Negative Edge, Capture Mode, Interupt
   TACTL = TASSEL_2 + MC_2;                // SMCLK, start in continuous mode
}

//------------------------------------------------------------------------------
// Outputs one byte using the Timer_A UART
//------------------------------------------------------------------------------
int Uart_Tx_Byte(uint8_t byte)
{  
   
   
   //check if buffer is full
   if(BufferFull(&mTxBuffer) == 1)
   {
      return -1;
   }
   if(mTxBuffer.mHeadPtr != mTxBuffer.mTailPtr)//if byte is being transimitted add to buffer
   {
      mTxBuffer.mData[mTxBuffer.mHeadPtr++%BUFF_SIZE] = byte; //add byte to cicular buffer
   }
   else
   {
      mTxData = byte | 0x100;              // Add end bit
      mTxData = mTxData << 1;              // Add start bit
      --mTxBuffer.mTailPtr;
      TACCR0 = TAR;                           // Current state of TA counter
      TACCR0 += mUartBitTime;                    // One bit time till first bit
      TACCTL0 = OUTMOD_1;               // Set TXD on EQU0, Int
   }
   return 0;
}


int Uart_Tx_Buffer(uint8_t* buffer, uint8_t size)
{
   int ret_status = 0;
   int i = 0;
   
   //ensure we are not in the middle of transmitting
   while(gCritical == 0x01) { }
   
   //disable tx timerinterrupts
   TACCTL0 &= ~CCIE;               // Set TXD on EQU0, Int
   while(i < size && ret_status != -1)
   {
      ret_status = Uart_Tx_Byte(buffer[i]);
      if(ret_status != -1)
         ++i;
   }
   TACCTL0 |= CCIE;               // Set TXD on EQU0, Int
   
   return i;
}

//Tx Uart TimerA0 Interrupt
interrupt (TIMERA0_VECTOR) Timer_A0_ISR(void)
{
   static uint8_t tx_bit_cnt = 10;
   
   gCritical = 0x01;
   TACCR0 += mUartBitTime;                    // Add Offset to CCRx
   if (tx_bit_cnt == 0) 
   {                    
      mTxBuffer.mTailPtr++;      
      //turn off timer interrupt
      if(mTxBuffer.mHeadPtr == mTxBuffer.mTailPtr)
      {
         TACCTL0 &= ~CCIE;                   // All bits TXed, disable interrupt
         gCritical = 0x00;                    // End critical section
      }
      else
      {
         mTxData = mTxBuffer.mData[mTxBuffer.mTailPtr%BUFF_SIZE] | 0x100; //update txdata and add end bit
         mTxData = mTxData << 1;  //Add start bit
      }
      tx_bit_cnt = 10;                      // Re-load bit counter
   }
   
   if( (TACCTL0 & CCIE) > 0)
   {

      if (mTxData & 0x01) 
      {
       TACCTL0 &= ~OUTMOD2;              // TX set pin to '1' 
      }
      else 
      {
       TACCTL0 |= OUTMOD2;               // TX set pin to '0' 
      }
      mTxData >>= 1;
      tx_bit_cnt--;
   }
   
}


// RX Uart TimerA1 Interrupt
interrupt (TIMERA1_VECTOR) Timer_A1_ISR(void)
{
   static unsigned char rx_bit_cnt = 9;//include start bit

   switch (TAIV) 
   {                           
      case TAIV_TACCR1:                        // TACCR1 CCIFG - UART RX
      {
         
         if (TACCTL1 & CAP) 
         {                                    //Capture falling edge
             TACCTL1 &= ~CAP;                 // Turn off capture and on compare
             TACCR1 += mUartHalfBitTime;       // Point the time to the middle of the bit
         }
         else 
         {
            TACCR1 += mUartBitTime;                // Add Offset to CCRx
             mRxData >>= 1;
             if (TACCTL1 & SCCI) 
             {            //input signal latch
                 mRxData |= 0x80;
             }
             
             rx_bit_cnt--;
             
            if (rx_bit_cnt == 0) 
            {             // All bits RXed?
               //if((gRxBuffer.mHeadPtr - gRxBuffer.mTailPtr) > 4)
                  //gRxFlags |= RX_OVERFLOW;
               gRxBuffer.mData[gRxBuffer.mHeadPtr++%(BUFF_SIZE)] = mRxData;
               gRxFlags |= RX_NOTEMPTY;
               mRxData = 0;
               rx_bit_cnt = 9;                // Re-load bit counter
               TACCTL1 |= CAP;              // Switch compare to capture mode
            }
            else if(rx_bit_cnt == 8)
            {
               if (TACCTL1 & SCCI) 
               {            //this should be zero rule out noise on the line
                  mRxData = 0;
                  rx_bit_cnt = 9;                // Re-load bit counter
                  TACCTL1 |= CAP;              // Switch compare to capture mode
               }
               //else continue receiving message
            }
         }
         break;
      }
   }
}
